
package ua.gradsoft.misc.jsoo;

import java.util.LinkedList;
import java.util.List;
import ua.gradsoft.javachecker.models.JavaMethodModel;
import ua.gradsoft.javachecker.models.MethodMatchingConversions;
import ua.gradsoft.javachecker.util.Pair;

/**
 *Here is changed algorithm for find best conversion,
 * aligned for JSOO  (ambiguouse possibility to have few
 * methods with same conversions with different names, so return
 * list instead one)
 * @author rssh
 */
public class MethodMatchingConversionsHelper {


    public static List<Pair<JavaMethodModel,MethodMatchingConversions>>  bests(List<Pair<JavaMethodModel, MethodMatchingConversions>> candidates)
    {
        List<Pair<JavaMethodModel,MethodMatchingConversions>> retval = bestsPhase1(candidates);
        if (retval.size()!=0) {
            return retval;
        }
        retval = bestPhase2(candidates);
        if (retval.size()!=0) {
            return retval;
        }
        retval = bestPhase3(candidates);
        return retval;
    }

    private static List<Pair<JavaMethodModel,MethodMatchingConversions>> bestsPhase1(List<Pair<JavaMethodModel, MethodMatchingConversions>> candidates)
    {
       Pair<JavaMethodModel,MethodMatchingConversions> minSpecific=null;
       List<Pair<JavaMethodModel,MethodMatchingConversions>> sameSpecifics = new LinkedList<Pair<JavaMethodModel,MethodMatchingConversions>>();
       for(Pair<JavaMethodModel, MethodMatchingConversions> candidate: candidates) {
           MethodMatchingConversions conversions = candidate.getSecond();
           if (conversions.getNBoxing()==0 && conversions.getNUnboxing()==0 && conversions.isVarArg()==false) {
               if (minSpecific==null) {
                   minSpecific=candidate;
                   sameSpecifics.add(candidate);
               }else{
                   if (isMoreSpecific(candidate.getSecond(),minSpecific.getSecond())){
                       minSpecific=candidate;
                       sameSpecifics.clear();
                       sameSpecifics.add(candidate);
                   }else if (isSameSpecific(candidate.getSecond(),minSpecific.getSecond())) {
                       sameSpecifics.add(candidate);
                   }
               }
           }
       }
       return sameSpecifics;
    }

   private static List<Pair<JavaMethodModel,MethodMatchingConversions>> bestPhase2(List<Pair<JavaMethodModel, MethodMatchingConversions>> candidates)
    {
       Pair<JavaMethodModel,MethodMatchingConversions> minSpecific=null;
       List<Pair<JavaMethodModel,MethodMatchingConversions>> sameSpecifics = new LinkedList<Pair<JavaMethodModel,MethodMatchingConversions>>();
       for(Pair<JavaMethodModel, MethodMatchingConversions> candidate: candidates) {
           MethodMatchingConversions conversions = candidate.getSecond();
           if (conversions.getNBoxing()==0 && conversions.getNUnboxing()==0) {
               if (minSpecific==null) {
                   minSpecific=candidate;
                   sameSpecifics.add(candidate);
               }else{
                   if (isMoreSpecific(candidate.getSecond(),minSpecific.getSecond())) {
                       minSpecific=candidate;
                       sameSpecifics.clear();
                       sameSpecifics.add(candidate);
                   }else if (isSameSpecific(candidate.getSecond(),minSpecific.getSecond())) {
                       sameSpecifics.add(candidate);
                   }
               }
           }
       }
       return sameSpecifics;
    }

    private static List<Pair<JavaMethodModel,MethodMatchingConversions>> bestPhase3(List<Pair<JavaMethodModel, MethodMatchingConversions>> candidates)
    {
       Pair<JavaMethodModel,MethodMatchingConversions> minSpecific=null;
       List<Pair<JavaMethodModel,MethodMatchingConversions>> sameSpecifics = new LinkedList<Pair<JavaMethodModel,MethodMatchingConversions>>();
       for(Pair<JavaMethodModel, MethodMatchingConversions> candidate: candidates) {
           MethodMatchingConversions conversions = candidate.getSecond();
           if (minSpecific==null) {
               minSpecific=candidate;
               sameSpecifics.add(candidate);
           }else {
               if (isMoreSpecific(candidate.getSecond(),minSpecific.getSecond())) {
                       minSpecific=candidate;
                       sameSpecifics.clear();
                       sameSpecifics.add(candidate);
               }else if (isSameSpecific(candidate.getSecond(),minSpecific.getSecond())) {
                       sameSpecifics.add(candidate);
               }
           }
       }
       return sameSpecifics;
    }

    private static boolean isMoreSpecific(MethodMatchingConversions x, MethodMatchingConversions y)
    {
        if (x.getNNarrows() < y.getNNarrows()) {
            return true;
        }
        if (x.getNRows() < y.getNRows()) {
            return true;
        }
        if (x.getNSupers() < y.getNSupers()) {
            return true;
        }
        return false;
    }

    private static boolean isSameSpecific(MethodMatchingConversions x, MethodMatchingConversions y)
    {
        return (x.getNNarrows() == y.getNNarrows()
                &&
            x.getNRows() == y.getNRows()
                &&
            x.getNSupers() == y.getNSupers()
                );
    }


}
